////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2009 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package spark.extensions.components {
	/*  
	
	*  @langversion 3.0
	*  @playerversion Flash 10
	*  @playerversion AIR 1.5
	*  @productversion Flex 4
	
	*/
	import flash.display.DisplayObject;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	import mx.core.mx_internal;
	import mx.events.FlexEvent;
	import mx.utils.ObjectUtil;
	
	import spark.components.List;
	import spark.components.TextInput;
	import spark.core.NavigationUnit;
	import spark.extensions.components.supportClasses.AutoCompleteController;
	import spark.extensions.components.supportClasses.AutoCompleteUtil;
	import spark.extensions.events.AutoCompleteEvent;
	import spark.layouts.VerticalLayout;
	import spark.layouts.supportClasses.LayoutBase;
	import spark.utils.LabelUtil;
	
	use namespace mx_internal;
	/**
 *  Dispatched when the drop-down list closes for any reason, such when 
 *  the user:
 *  <ul>
 *      <li>Selects an item in the drop-down list.</li>
 *      <li>Clicks outside of the drop-down list.</li>
 *  </ul>
 *
 *  @eventType spark.extensions.events.AutoCompleteEvent.CLOSE
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
	[Event(name="close", type="spark.extensions.events.AutoCompleteEvent")]
	/**
 *  Dispatched when the display the drop-down list occurs.  
 *  It is also dispatched if the user
 *  uses Control-Down to open the dropDown.
 *
 *  @eventType spark.extensions.events.AutoCompleteEvent.OPEN
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */	
	[Event(name="open", type="spark.extensions.events.AutoCompleteEvent")]
	/**
 *  Dispatched when we had to refresh our collection due to user input into the textInput
 *
 *  @eventType spark.extensions.events.AutoCompleteEvent.REFILTER
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
	[Event(name="refilter", type="spark.extensions.events.AutoCompleteEvent")]
/**
 *  Skin state for the open state of the AutoComplete control.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
	[SkinState("open")]
	//--------------------------------------
//  Excluded APIs
//--------------------------------------
	[Exclude(name="allowMultipleSelection", kind="property")]
	[Exclude(name="selectedIndices", kind="property")]
	[Exclude(name="selectedItems", kind="property")]
	[Exclude(name="labelFunction", kind="property")]
	/**
 *  The AutoComplete control contains a drop-down list
 *  from which the user can select a single value.
 *
 *  <p>The AutoComplete control consists of a prompt area, and drop-down-list, 
 *  The prompt area displays a prompt String, or the selected item 
 *  in the drop-down-list.</p> 
 *  @see spark.extensions.skins.AutoCompleteSkin
 *  @see spark.extensions.components.supportClasses.AutoCompleteController
 *
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
	public class AutoComplete extends List {	
	//--------------------------------------------------------------------------
    //
    //  Skin Parts
    //
    //--------------------------------------------------------------------------    
    /**
     *  A skin part that holds the prompt, the user input or the text of the selected item. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		[SkinPart(required="true")]
		public var textInputElement:TextInput;
		
		[SkinPart(required="false")]
		public var dropDown:DisplayObject;	    
	/**
     *  Constructor. 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		public function AutoComplete() {
			super();
			super.allowMultipleSelection = false;			
			controller = new AutoCompleteController();
		}
	//--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------
		mx_internal static const PAGE_SIZE : int = 5;
		mx_internal var userProposedSelectedIndex : Number = -1;
		
		private var labelChanged:Boolean = false;
		
		
		private var _controller:AutoCompleteController;	
		
		protected function get controller():AutoCompleteController {
			return _controller;
		}
		
		protected function set controller(value:AutoCompleteController):void {
			if (_controller == value) return;				
			_controller = value;							
			_controller.addEventListener(AutoCompleteEvent.OPEN, dropDownController_openHandler);
			_controller.addEventListener(AutoCompleteEvent.CLOSE, dropDownController_closeHandler);
			_controller.addEventListener(AutoCompleteEvent.REFILTER , dropDownController_refilterHandler);			
			if (textInputElement) _controller.textInputElement = textInputElement;
			if (dropDown) _controller.dropDown = dropDown;	
		}
		
		public function get prompt():String  {
			return _controller.prompt;
		}
		
		public function set prompt(value:String):void {
			if (_controller.prompt == value) return;	    		
			_controller.prompt = value;
			labelChanged = true;
			invalidateProperties();
		}
		
		private var _match : String = AutoCompleteUtil.MATCH_ANY_PART;
		public function set match(value : String):void{
			if (value == '' || _match == value) return;
			_match = value;
		} 
		public function get match():String{
			return _match;
		}    
    //--------------------------------------------------------------------------
    //
    //  Overridden Properties
    //
    //--------------------------------------------------------------------------
		override public function set allowMultipleSelection(value:Boolean):void { 
			return;
		}
		
		override public function get baselinePosition():Number {	    	
			return NaN;
		}
		
		override public function set dataProvider(value:IList):void {	
			if (dataProvider ===  value) return;
			super.dataProvider = value;	    		    	
			labelChanged = true;
			invalidateProperties();	    	
		}
		
		override public function set enabled(value:Boolean):void {
			if (value == enabled) return;
			super.enabled = value;
			if (textInputElement) textInputElement.enabled = value;
		}
		
		override public function set labelField(value:String):void {
			if (labelField == value) return;
			super.labelField = value;	    	
			labelChanged = true;
			invalidateProperties();
		}
		
		override public function set labelFunction(value:Function):void {
			if (labelFunction == value) return;
			super.labelFunction = value;
			labelChanged = true;
			invalidateProperties();
		}
		
		override public function get layout():LayoutBase {	    	
			return (dataGroup) ? super.layout : new VerticalLayout();
		}
		
    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------  
	/**
     *  @public
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		public function openDropDown():void {
			controller.openDropDown();
		}
	/**
     *  @public
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */	
		public function closeDropDown(commit:Boolean):void {
			controller.closeDropDown(commit);
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */	
		private function updateTextInputElement():void {			
			if (textInputElement) {				
					/*(dataProvider as ArrayCollection).filterFunction = filterDataProvider;
					(dataProvider as ArrayCollection).refresh();*/
					if (selectedItem) {
						textInputElement.text = LabelUtil.itemToLabel(selectedItem, labelField, labelFunction);	    				
					} else {
						textInputElement.text = _controller.prompt;
					}
			}	
		}
    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------
		override protected function commitMultipleSelection():void {
			super.commitMultipleSelection();
			updateTextInputElement();
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */	
		override protected function commitProperties():void {
			super.commitProperties();
			if (labelChanged) {
				labelChanged = false;
				updateTextInputElement();
			}
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */	
		override protected function getCurrentSkinState():String {
			return !enabled ? "disabled" : controller.isOpen ? "open" : "normal";
		}   
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */	
		override protected function partAdded(partName:String, instance:Object):void {	    	
			super.partAdded(partName, instance);
			if (instance == textInputElement) {
				controller.textInputElement = textInputElement;	    			
				labelChanged = true;
				invalidateProperties();
			}	    	
			if (instance == dropDown && controller) controller.dropDown = dropDown;
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		override protected function partRemoved(partName:String, instance:Object):void {
			if (controller) {	    	
				if (instance == textInputElement)  controller.textInputElement = null;
				if (instance == dropDown) controller.dropDown = null;
			}	     	
			super.partRemoved(partName, instance);
		}
    //--------------------------------------------------------------------------
    //
    //  Event handling
    //
    //--------------------------------------------------------------------------
		override protected function item_clickHandler(event:MouseEvent):void {
			super.item_clickHandler(event);
			userProposedSelectedIndex = selectedIndex;
			closeDropDown(true);
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		override protected function keyDownHandler(event:KeyboardEvent) : void {
			if(!enabled) return;
			if (!controller.processKeyDown(event)) {
				var navigationUnit:uint = event.keyCode;
				if (!NavigationUnit.isNavigationUnit(navigationUnit)) return;
				var proposedNewIndex:int = -1;
				if (controller.isOpen) {   
					proposedNewIndex = layout.getNavigationDestinationIndex(userProposedSelectedIndex, navigationUnit, arrowKeysWrapFocus);	                
					if (proposedNewIndex != -1) {
						itemSelected(userProposedSelectedIndex, false);
						userProposedSelectedIndex = proposedNewIndex;
						itemSelected(userProposedSelectedIndex, true);
						ensureIndexIsVisible(userProposedSelectedIndex);
					}	                
				}
			}
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		private function dropDownController_refilterHandler(e:AutoCompleteEvent = null):void{
			if (dataProvider is ArrayCollection){
				(dataProvider as ArrayCollection).filterFunction = filterDataProvider;
				(dataProvider as ArrayCollection).refresh();
			}else {
				trace('[AutoComplete] : for now, we support only ArrayCollection as dataProvider.');
			}
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		private function filterDataProvider(item : Object):Boolean{
			if (textInputElement.text == '' || textInputElement.text == _controller.prompt) return true;
			switch (_match){
				case AutoCompleteUtil.MATCH_ANY_PART:	    			
					return item[super.labelField].match( new RegExp(textInputElement.text, 'i'));
					break;
				case AutoCompleteUtil.MATCH_BEGINNING:
					return (item[super.labelField] as String).substr(0 , textInputElement.text.length) == textInputElement.text;
					break;
				case AutoCompleteUtil.MATCH_WORD:
					break;
			}
			return true;	    	
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		override protected function focusOutHandler(event:FocusEvent):void {
			controller.processFocusOut(event);
			super.focusOutHandler(event);
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		protected function dropDownController_openHandler(event:AutoCompleteEvent):void {
			addEventListener(FlexEvent.UPDATE_COMPLETE, open_updateCompleteHandler);
			userProposedSelectedIndex = selectedIndex;
			invalidateSkinState();	
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		private function open_updateCompleteHandler(event:FlexEvent):void {	
			removeEventListener(FlexEvent.UPDATE_COMPLETE, open_updateCompleteHandler);
			ensureIndexIsVisible(selectedIndex);
			dropDownController_refilterHandler();	    	
			dispatchEvent(new AutoCompleteEvent(AutoCompleteEvent.OPEN));
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		protected function dropDownController_closeHandler(event:AutoCompleteEvent):void {
			addEventListener(FlexEvent.UPDATE_COMPLETE, close_updateCompleteHandler);
			invalidateSkinState();	    	
			if (!event.isDefaultPrevented()) {				
				selectedIndex = userProposedSelectedIndex;
			}
		}
	/**
     *  @private
     * 
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
		private function close_updateCompleteHandler(event:FlexEvent):void {	
			removeEventListener(FlexEvent.UPDATE_COMPLETE, close_updateCompleteHandler);
			dropDownController_refilterHandler();
			dispatchEvent(new AutoCompleteEvent(AutoCompleteEvent.CLOSE));
		}
		
	}
}